package com.obelisco.vista.componentes;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Vector;

import nextapp.echo2.app.Alignment;
import nextapp.echo2.app.CheckBox;
import nextapp.echo2.app.Column;
import nextapp.echo2.app.Component;
import nextapp.echo2.app.Extent;
import nextapp.echo2.app.Font;
import nextapp.echo2.app.ImageReference;
import nextapp.echo2.app.Insets;
import nextapp.echo2.app.ResourceImageReference;
import nextapp.echo2.app.Table;
import nextapp.echo2.app.event.ActionEvent;
import nextapp.echo2.app.event.ActionListener;
import nextapp.echo2.app.event.TableModelEvent;
import nextapp.echo2.app.event.TableModelListener;
import nextapp.echo2.app.layout.ColumnLayoutData;
import nextapp.echo2.app.layout.TableLayoutData;
import nextapp.echo2.app.list.DefaultListSelectionModel;
import nextapp.echo2.app.list.ListModel;
import nextapp.echo2.app.list.ListSelectionModel;
import nextapp.echo2.app.table.TableCellRenderer;
import nextapp.echo2.app.table.TableColumn;
import nextapp.echo2.app.table.TableColumnModel;

import org.apache.commons.beanutils.MethodUtils;
import org.apache.commons.beanutils.PropertyUtils;

import com.jgoodies.binding.adapter.echo.AbstractTableAdapter;
import com.jgoodies.binding.list.echo.SelectionInList;
import com.obelisco.vista.controles.Button;
import com.obelisco.vista.controles.DateField;
import com.obelisco.vista.controles.Label;
import com.obelisco.vista.controles.SelectField;
import com.obelisco.vista.controles.TextField;

import echopointng.BorderEx;
import echopointng.ContainerEx;
import echopointng.ExtentEx;
import echopointng.table.DefaultPageableSortableTableModel;
import echopointng.table.PageableSortableTable;
import echopointng.table.PageableTableNavigation;
import echopointng.table.SortableTableHeaderRenderer;
import echopointng.table.SortableTableModel;

public class TablaDatosSimple extends ContainerEx {

	private PageableSortableTable tabla;

	private AbstractTableAdapter adapter;

	private DefaultPageableSortableTableModel modeloTabla;

	private PageableTableNavigation tableScroller;

	private DefaultListSelectionModel defaultListSelectionModel;

	private Vector<TablaDatosListener> listeners = new Vector<TablaDatosListener>();
	
	////////////////////////////////
	// Agregado por Edgar Pastran
	////////////////////////////////
	private HashMap<TablaDatosListener, ArrayList<HashMap<Integer, Component>>> mapaTablaDatosComponentes = new HashMap<TablaDatosListener, ArrayList<HashMap<Integer, Component>>>();
	////////////////////////////////
	
	private int filaSeleccionada = -1;
	
	private int cantidadFilas = 10;
	
	private boolean tableScrollerVisible = true;

	private Component container;
	
	private List<String> alineacionCampos;
	
	private Map<String, String> camposMostrar;
	
	private List<Object> listaItems;
	
	private String campoToolTipText;
	
	
	public TablaDatosSimple() {
		initComponents();
	}

	// Se activa cuando alguien selecciona una fila de la Tabla
	private ActionListener tablaActionListener = new ActionListener() {

		public void actionPerformed(ActionEvent e) {
			Object objeto = getFilasSeleccionadas();
			
			if (objeto instanceof List<?>) 
				distribuirSeleccion((List<Object>)objeto);
			else
				distribuirSeleccion(objeto);
			
			//filaSeleccionada = tabla.getSelectionModel().getMinSelectedIndex();

			// filaSeleccionada =
			// modeloTabla.toSortedViewRowIndex(filaSeleccionada);

			//			filaSeleccionada = modeloTabla
			//					.toUnsortedModelRowIndex(filaSeleccionada);
		}

	};

	private TableModelListener tableModelListener = new TableModelListener() {

		private boolean isSorted = false;

		List<Integer> listaSeleccionados = new ArrayList<Integer>();

		public void tableChanged(TableModelEvent event) {

			if (hayFilasSeleccionadas()) {
				// TODO: Optimizar este codigo
				if (!isSorted) {
					isSorted = true;
					listaSeleccionados.clear();
					for (int i = tabla.getSelectionModel().getMinSelectedIndex(); i <= tabla.getSelectionModel().getMaxSelectedIndex(); i++)
						if (tabla.getSelectionModel().isSelectedIndex(i))
							listaSeleccionados.add(i);
				} 
				
				if (modeloTabla.getCurrentSortColumn() == -1)
					isSorted = false;

				defaultListSelectionModel.clearSelection();
				for (int i = 0; i < listaSeleccionados.size(); i++) {
					int indice = listaSeleccionados.get(i);
					if (isSorted)
						defaultListSelectionModel.setSelectedIndex(modeloTabla.toSortedViewRowIndex(indice), true);
					else
						defaultListSelectionModel.setSelectedIndex(indice, true);
				}

			}
		}
	};

	private SortableTableHeaderRenderer encabezadoTableCellRenderer = new SortableTableHeaderRenderer() {

		private ImageReference upArrowImage = new ResourceImageReference("/echopointng/resource/images/ArrowUp.gif");

		private ImageReference downArrowImage = new ResourceImageReference("/echopointng/resource/images/ArrowDown.gif");

		protected Button getSortButton(String label, int column, SortableTableModel model) {

			ImageReference icon = null;
			if (model.getCurrentSortColumn() == column) {
				int sortDirective = model.getSortDirective(column);
				if (sortDirective == SortableTableModel.ASCENDING)
					icon = upArrowImage;
				else if (sortDirective == SortableTableModel.DESCENDING)
					icon = downArrowImage;
			}

			Button button = new Button(icon);
			button.setStyleName("Obelisco.Boton.TablaDatos");
			button.setText(label);
			button.addActionListener(getSortButtonListener(column, model));
			button.setTextPosition(new Alignment(Alignment.LEFT,Alignment.DEFAULT));

			return button;
		}
		
	};

	private TableCellRenderer tableCellRenderer = new TableCellRenderer() {

		public Component getTableCellRendererComponent(Table table, Object value, int column, int row) {
			if (modeloTabla.getCurrentPage() != 0)
				row = modeloTabla.toUnpagedModelRowIndex(row);
			if (modeloTabla.getCurrentSortColumn() != -1)
				row = modeloTabla.toUnsortedModelRowIndex(row);
			
        	TableLayoutData layoutData = new TableLayoutData();
			Alignment alineacion = new Alignment(Alignment.CENTER,Alignment.CENTER);

			Object objeto = adapter.getRow(row);
			
			Component componente = distribuirDibujarCelda(objeto,value, row, column, alineacion);

			if (componente == null) {
				String nombreEstilo = getRowStyle(row);
				
				Label label = new Label(value != null ? value.toString() : "");
				label.setStyleName(nombreEstilo);
				try {
					if (campoToolTipText != null) {
						Object valorCampoToolTipText = PropertyUtils.getNestedProperty(objeto, campoToolTipText);
						if (valorCampoToolTipText != null &&
							valorCampoToolTipText.getClass().equals(String.class))
							label.setToolTipText(valorCampoToolTipText.toString());
					}
				}
				catch (Exception e) {
				}
				componente = label;
				
			} else {
				//TODO: Colocar la Alineacion para el Componente que definio el Usuario
				layoutData.setAlignment(alineacion);
				componente.setLayoutData(layoutData);
			}
			if(alineacionCampos!= null && !alineacionCampos.isEmpty())
        		if (alineacionCampos.get(column).equals("center"))
        			layoutData.setAlignment(new Alignment(Alignment.CENTER, Alignment.CENTER));
        		else if (alineacionCampos.get(column).equals("left"))
        			layoutData.setAlignment(new Alignment(Alignment.LEFT, Alignment.CENTER));
        		else if (alineacionCampos.get(column).equals("right"))
        			layoutData.setAlignment(new Alignment(Alignment.RIGHT, Alignment.CENTER ));				
			componente.setLayoutData(layoutData);
			return componente;
		}
	};
	
	public ListSelectionModel getSelectionModel()
	{
		return tabla.getSelectionModel();
	}

	private void initComponents() {

		container = new Column();
		//setHeightStretched(true);
			
		ColumnLayoutData layoutData = new ColumnLayoutData();
		layoutData.setAlignment(new Alignment(Alignment.CENTER, Alignment.CENTER));
		layoutData.setInsets(new Insets(0,10,0,0));

		tabla = new PageableSortableTable();
		tabla.setDefaultHeaderRenderer(encabezadoTableCellRenderer);
		tabla.addActionListener(tablaActionListener);
		
		defaultListSelectionModel = new DefaultListSelectionModel();
		tabla.setSelectionModel(defaultListSelectionModel);
	
		defaultListSelectionModel.setSelectionMode(ListSelectionModel.MULTIPLE_SELECTION);

		tabla.setStyleName("Obelisco.TablaDatos");
		tabla.setBorder(new BorderEx());
		tabla.setHeight(new ExtentEx("90%"));
		tabla.setWidth(new ExtentEx("95%"));
		tabla.setLayoutData(layoutData);	
		add(container);
	}

	class GenericTableModel extends AbstractTableAdapter {

		ListModel listModel;

		String[] headers = {};

		String[] fields = {};

		public GenericTableModel(ListModel listModel, String[] headers,	String[] fields) {
			super(listModel, headers);
			this.listModel = listModel;
			this.headers = headers;
			this.fields = fields;			
		}

		public Object getValueAt(int columnIndex, int rowIndex) {
			Object objeto = getRow(rowIndex);
			try {
				String atributo = fields[columnIndex];
				//Object valor = PropertyUtils.getSimpleProperty(objeto, atributo);
				Object valor = PropertyUtils.getNestedProperty(objeto, atributo);
				return valor;
			} catch (Exception e) {
				e.printStackTrace();
				return null;
			}
		}
	}

	/*public void setItems(List items, Map<String, String> camposMostrar) {

		SelectionInList selectionInList = new SelectionInList(items);

		String[] headers = new String[camposMostrar.size()];
		String[] fields = new String[camposMostrar.size()];
		camposMostrar.keySet().toArray(headers);
		camposMostrar.values().toArray(fields);

		GenericTableModel model = new GenericTableModel(selectionInList, headers, fields);
		setAdapter(model);

	}*/
	
	public void setItems(Collection<Object> items, Map<String, String> camposMostrar, List<String> alineacionCampos) {
		this.camposMostrar = camposMostrar;
		SelectionInList selectionInList;
		if (items != null)
			if(items instanceof List<?>) {
				this.listaItems = (List<Object>)items;
				selectionInList = new SelectionInList((List<Object>)items);
			}
			else
				selectionInList = new SelectionInList(new ArrayList<Object>(items));
		else
			selectionInList = new SelectionInList();
		
		this.alineacionCampos = alineacionCampos;
		String[] headers = new String[camposMostrar.size()];
		String[] fields = new String[camposMostrar.size()];		
		camposMostrar.keySet().toArray(headers);
		camposMostrar.values().toArray(fields);
		GenericTableModel model = new GenericTableModel(selectionInList, headers, fields);
		setAdapter(model);
	}
	
	public List<Object> getItems() {
		List<Object> listaObjetos = new ArrayList<Object>();
		try {						
			Map<Integer, TablaDatosRendererComponent> mapaRender = (Map<Integer, TablaDatosRendererComponent>)MethodUtils.invokeMethod(adapter,"getTablaDatosRendererComponent",null);
			if(mapaRender != null) {
				int cantidadRegistros = this.adapter.getRowCount();		
				for (int i = 0; i < cantidadRegistros; i++) {
					Object objeto = this.adapter.getRow(i);
					int cantidadColumnas = this.adapter.getColumnCount();
					for (int j = 0; j < cantidadColumnas; j++) {
						if(mapaRender.containsKey(j)) {
							TablaDatosRendererComponent renderComponent = mapaRender.get(j);
							Class<?> claseRenderComponent = renderComponent.getClase();
							String nombreAtributo = renderComponent.getNombreAtributo();
							if (com.obelisco.vista.controles.SelectField.class.equals(claseRenderComponent)){						
								SelectField combo = (SelectField)tabla.getCellComponent(j,i);
								Object itemSelecciondado = combo.getSelectedItem();
								PropertyUtils.setNestedProperty(objeto, nombreAtributo.toLowerCase(), itemSelecciondado);
				        	}
							else if (com.obelisco.vista.controles.TextField.class.equals(claseRenderComponent)){
								TextField textField = (TextField)tabla.getCellComponent(j,i);
								String valor = textField.getText();
								PropertyUtils.setNestedProperty(objeto, nombreAtributo.toLowerCase(), valor);
							}
							else if (nextapp.echo2.app.CheckBox.class.equals(claseRenderComponent)){
								CheckBox check = (CheckBox)tabla.getCellComponent(j,i);
								PropertyUtils.setNestedProperty(objeto, nombreAtributo.toLowerCase(), check.isSelected());
							}
							else if (com.obelisco.vista.controles.DateField.class.equals(claseRenderComponent)){
								DateField dateField = (DateField)tabla.getCellComponent(j,i);
								java.util.Date fecha = new Date(dateField.getSelectedDate().getTimeInMillis());
								PropertyUtils.setNestedProperty(objeto, nombreAtributo.toLowerCase(), fecha);					
							}
						}
					}
					listaObjetos.add(objeto);
				}
			}
			else {
				int cantidadRegistros = this.adapter.getRowCount();		
				for (int i = 0; i < cantidadRegistros; i++)
					listaObjetos.add(this.adapter.getRow(i));
			}	
		}
		catch (Exception e) {
			int cantidadRegistros = this.adapter.getRowCount();		
			for (int i = 0; i < cantidadRegistros; i++)
				listaObjetos.add(this.adapter.getRow(i));
		}
		
		return listaObjetos.isEmpty() ? null : listaObjetos;
	}

	
	/*public void setItems(Set items, Map<String, String> camposMostrar) {

		SelectionInList selectionInList = new SelectionInList(new ArrayList(items));

		String[] headers = new String[camposMostrar.size()];
		String[] fields = new String[camposMostrar.size()];
		camposMostrar.keySet().toArray(headers);
		camposMostrar.values().toArray(fields);

		GenericTableModel model = new GenericTableModel(selectionInList, headers, fields);
		setAdapter(model);

	}*/

	public AbstractTableAdapter getAdapter() {
		return adapter;
	}

	public void setAdapter(AbstractTableAdapter adapter) {
		this.adapter = adapter;		
		container.removeAll();
				
		if (adapter != null && adapter.getRowCount() != 0) {
			container.add(tabla);
			modeloTabla = new DefaultPageableSortableTableModel(adapter);
			modeloTabla.addTableModelListener(tableModelListener);
			modeloTabla.setRowsPerPage(cantidadFilas);			
			modeloTabla.setCurrentPage(0);
			tabla.setModel(modeloTabla);
			tabla.setDefaultRenderer(Object.class, tableCellRenderer);
			
			if(tableScrollerVisible) {
				tableScroller = new PageableTableNavigation(tabla);
				Alignment alineacion = new Alignment(Alignment.CENTER, Alignment.TOP);
				tableScroller.setAlignment(alineacion);
				tableScroller.setInsets(new Insets(0,10,0,0));				
				container.add(tableScroller);						
			}			

			TableColumnModel modeloColumna = tabla.getColumnModel();
			for (int i = 0; i < adapter.getColumnCount(); i++) {
				TableColumn columna = modeloColumna.getColumn(i);
				columna.setHeaderRenderer(encabezadoTableCellRenderer);
			}

		} else {
					
			Label lblSinDatos = new Label("No Hay Informacion Para Mostrar");
			lblSinDatos.setFont(new Font(null, 1, new Extent(20)));
			ColumnLayoutData layoutData = new ColumnLayoutData();
			layoutData.setHeight(new Extent(100, Extent.PERCENT));
			//layoutData.setInsets(new Insets(null, new Extent(15, Extent.PERCENT), null, null));
			Alignment alineacion = new Alignment(Alignment.CENTER, Alignment.CENTER);
			layoutData.setAlignment(alineacion);
			lblSinDatos.setLayoutData(layoutData);
			container.add(lblSinDatos);			
		}
	}

	public void setFilasSeleccionadas(List<Object> listaObjetos) {
		for (int i = 0; i < listaObjetos.size(); i++) {
			boolean isIgual = false;
			int posicion = 0;
			for (int j = 0; j < adapter.getRowCount(); j++)
				if (adapter.getRow(j).equals(listaObjetos.get(i))) {
					isIgual = true;
					posicion = j;
				}

			if (isIgual)
				defaultListSelectionModel.setSelectedIndex(posicion, true);
		}
	}

	public Object getFilasSeleccionadas() {
		List<Object> seleccionados = new ArrayList<Object>();
		if (!isSeleccionMultiple()) {
			// if (filaSeleccionada >= 0) {
			if (tabla.getSelectionModel().getMinSelectedIndex() >= 0) {

				if (tabla.getSelectionModel().getMinSelectedIndex() > modeloTabla.getRowCount())
					tabla.getSelectionModel().setSelectedIndex(modeloTabla.getRowCount()-1, true);										
				
				filaSeleccionada = tabla.getSelectionModel().getMinSelectedIndex();
				if (modeloTabla.getCurrentPage() != 0)
					filaSeleccionada = modeloTabla.toUnpagedModelRowIndex(filaSeleccionada);
				if (modeloTabla.getCurrentSortColumn() != -1)
					filaSeleccionada = modeloTabla.toUnsortedModelRowIndex(filaSeleccionada);			
				
				return adapter.getRow(filaSeleccionada);
				
			} else
				return null;
		} else if (tabla.getSelectionModel().getMinSelectedIndex() >= 0)
			for (int i = tabla.getSelectionModel().getMinSelectedIndex(); i <= tabla.getSelectionModel().getMaxSelectedIndex(); i++)
				if (tabla.getSelectionModel().isSelectedIndex(i)) {
					int index = i;
					if (modeloTabla.getCurrentPage() != 0)
						index = modeloTabla.toUnpagedModelRowIndex(index);
					if (modeloTabla.getCurrentSortColumn() != -1)
						index = modeloTabla.toUnsortedModelRowIndex(index);				
					seleccionados.add(adapter.getRow(index));
				}
		return seleccionados;
	}

	public void setSelectionEnabled(boolean selectionEnabled) {
		tabla.setSelectionEnabled(selectionEnabled);
	}

	public boolean isSelectionEnabled() {
		return tabla.isSelectionEnabled();
	}

	public void clearSelection() {
		tabla.getSelectionModel().clearSelection();
	}

	public boolean hayFilasSeleccionadas() {
		return tabla.getSelectionModel().getMinSelectedIndex() >= 0;
	}

	public void addTablaDatosActionListener(ActionListener tablaDatosActionListener) {
		tabla.addActionListener(tablaDatosActionListener);
	}

	public void setSeleccionMultiple(boolean modo) {
		defaultListSelectionModel.setSelectionMode(modo ? DefaultListSelectionModel.MULTIPLE_SELECTION : DefaultListSelectionModel.SINGLE_SELECTION);			
	}

	public boolean isSeleccionMultiple() {
		return defaultListSelectionModel.getSelectionMode() == DefaultListSelectionModel.MULTIPLE_SELECTION;
	}

	public Component getComponente(int fila, int columna) {
		Component componente = tabla.getCellComponent(columna, fila);
		return componente;
	}

	private String getRowStyle(int r) {
		return r % 2 == 0 ? "Obelisco.TablaDatos.Par" : "Obelisco.TablaDatos.Impar";
	}
	
	public void addTablaDatosListener(TablaDatosListener listener) {
		listeners.add(listener);
		mapaTablaDatosComponentes.put(listener, new ArrayList<HashMap<Integer, Component>>());
	}

	public void removeTablaDatosListener(TablaDatosListener listener) {
		listeners.remove(listener);
		mapaTablaDatosComponentes.remove(listener);
	}

	public void distribuirSeleccion(Object objeto) {
		for (TablaDatosListener tablaDatosListener : listeners) {		
			tablaDatosListener.seleccionObjeto(objeto);
		}
	}
	
	public void distribuirSeleccion(List<Object> objetos) {
		for (TablaDatosListener tablaDatosListener : listeners)		
			tablaDatosListener.seleccionObjeto(objetos);
	}
	
	public Component distribuirDibujarCelda(Object objeto, Object value, int fila, int columna, Alignment alineacion) {
		Component aDibujar = null;		
		for (TablaDatosListener l : listeners) {						
			//aDibujar = l.onDibujarCelda(objeto,value, fila, columna, alineacion);
			////////////////////////////////
			// Agregado por Edgar Pastran
			////////////////////////////////
			ArrayList<HashMap<Integer, Component>> listaComponentes = mapaTablaDatosComponentes.get(l);
			if (listaComponentes.size() > fila) {
				HashMap<Integer, Component> mapaComponentesFila = listaComponentes.get(fila);
				if (mapaComponentesFila.get(columna) != null)
					aDibujar = mapaComponentesFila.get(columna);
				else {
					aDibujar = l.onDibujarCelda(objeto,value, fila, columna, alineacion);
					if (aDibujar != null)
						mapaComponentesFila.put(columna, aDibujar);
				}
			}
			else {
				aDibujar = l.onDibujarCelda(objeto,value, fila, columna, alineacion);
				if (aDibujar != null) {
					HashMap<Integer, Component> mapaComponentesFila = new HashMap<Integer, Component>();
					mapaComponentesFila.put(columna, aDibujar);
					listaComponentes.add(fila, mapaComponentesFila);
				}
			}
			////////////////////////////////
		}
		return aDibujar;
	}

	////////////////////////////////
	// Agregado por Edgar Pastran
	////////////////////////////////
	private void eliminarComponenteFila(int filaEliminar) {		
		for (TablaDatosListener tablaDatosListener : listeners) {
			ArrayList<HashMap<Integer, Component>>  listaComponentes = mapaTablaDatosComponentes.get(tablaDatosListener);
			listaComponentes.remove(filaEliminar);
		}		
	}
	
	public void eliminarComponentesFilasSeleccionadas() {
		if (!isSeleccionMultiple()) {
			if (this.filaSeleccionada >= 0)
				this.eliminarComponenteFila(this.filaSeleccionada);
		} 
		else if (tabla.getSelectionModel().getMinSelectedIndex() >= 0) {
			for (int i = tabla.getSelectionModel().getMaxSelectedIndex(); i >= tabla.getSelectionModel().getMinSelectedIndex(); i--) {				
				if (tabla.getSelectionModel().isSelectedIndex(i)) {
					int index = i;
					if (modeloTabla.getCurrentPage() != 0)
						index = modeloTabla.toUnpagedModelRowIndex(index);
					if (modeloTabla.getCurrentSortColumn() != -1)
						index = modeloTabla.toUnsortedModelRowIndex(index);				
					this.eliminarComponenteFila(index);
				}
			}
		}		
	}
	////////////////////////////////
	
	public boolean isTableScrollerVisible() {
		return tableScrollerVisible;
	}

	public void setTableScrollerVisible(boolean tableScrollerVisible) {
		this.tableScrollerVisible = tableScrollerVisible;
	}
	
	public void limpiarSeleccionTabla(){
		tabla.getSelectionModel().clearSelection();
		filaSeleccionada = -1;		
	}

	public int getCantidadFilas() {
		return cantidadFilas;
	}

	public void setCantidadFilas(int cantidadFilas) {
		this.cantidadFilas = cantidadFilas;		
	}

	public Map<String, String> getCamposMostrar() {
		return camposMostrar;
	}

	public void setCamposMostrar(Map<String, String> camposMostrar) {
		this.camposMostrar = camposMostrar;
	}

	public List<String> getAlineacionCampos() {
		return alineacionCampos;
	}

	public void setAlineacionCampos(List<String> alineacionCampos) {
		this.alineacionCampos = alineacionCampos;
	}

	public List<Object> getListaItems() {
		return listaItems;
	}

	public void setListaItems(List<Object> listaItems) {
		this.listaItems = listaItems;
	}

	public TableModelListener getTableModelListener() {
		return tableModelListener;
	}

	public void setTableModelListener(TableModelListener tableModelListener) {
		this.tableModelListener = tableModelListener;
	}

	public PageableSortableTable getTabla() {
		return tabla;
	}

	public void setTabla(PageableSortableTable tabla) {
		this.tabla = tabla;
	}

	public DefaultPageableSortableTableModel getModeloTabla() {
		return modeloTabla;
	}

	public void setModeloTabla(DefaultPageableSortableTableModel modeloTabla) {
		this.modeloTabla = modeloTabla;
	}

	public String getCampoToolTipText() {
		return campoToolTipText;
	}

	public void setCampoToolTipText(String campoToolTipText) {
		this.campoToolTipText = campoToolTipText;
	}
}
